<?php
/*
 * CKFinder
 * ========
 * http://ckfinder.com
 * Copyright (C) 2007-2011, CKSource - Frederico Knabben. All rights reserved.
 *
 * The software, this file and its contents are subject to the CKFinder
 * License. Please read the license.txt file before using, installing, copying,
 * modifying or distribute this file or part of its contents. The contents of
 * this file is part of the Source Code of CKFinder.
 */
if (!defined('IN_CKFINDER')) exit;

/**
 * @package CKFinder
 * @subpackage Config
 * @copyright CKSource - Frederico Knabben
 */

/**
 * Folder view mask
 */
define('CKFINDER_CONNECTOR_ACL_FOLDER_VIEW',1);
define('CKFINDER_CONNECTOR_ACL_FOLDER_CREATE',2);
define('CKFINDER_CONNECTOR_ACL_FOLDER_RENAME',4);
define('CKFINDER_CONNECTOR_ACL_FOLDER_DELETE',8);
define('CKFINDER_CONNECTOR_ACL_FILE_VIEW',16);
define('CKFINDER_CONNECTOR_ACL_FILE_UPLOAD',32);
define('CKFINDER_CONNECTOR_ACL_FILE_RENAME',64);
define('CKFINDER_CONNECTOR_ACL_FILE_DELETE',128);

/**
 * This class keeps ACL configuration
 *
 * @package CKFinder
 * @subpackage Config
 * @copyright CKSource - Frederico Knabben
 */
class CKFinder_Connector_Core_AccessControlConfig
{

	/**
	 * array with ACL entries
	 *
	 * @var array[string]string
	 * @access private
	 */
	private $_aclEntries = array();

	function __construct($accessControlNodes)
	{
		foreach ($accessControlNodes as $node) {
			$_folderView = isset($node['folderView']) ? CKFinder_Connector_Utils_Misc::booleanValue($node['folderView']) : false;
			$_folderCreate = isset($node['folderCreate']) ? CKFinder_Connector_Utils_Misc::booleanValue($node['folderCreate']) : false;
			$_folderRename = isset($node['folderRename']) ? CKFinder_Connector_Utils_Misc::booleanValue($node['folderRename']) : false;
			$_folderDelete = isset($node['folderDelete']) ? CKFinder_Connector_Utils_Misc::booleanValue($node['folderDelete']) : false;
			$_fileView = isset($node['fileView']) ? CKFinder_Connector_Utils_Misc::booleanValue($node['fileView']) : false;
			$_fileUpload = isset($node['fileUpload']) ? CKFinder_Connector_Utils_Misc::booleanValue($node['fileUpload']) : false;
			$_fileRename = isset($node['fileRename']) ? CKFinder_Connector_Utils_Misc::booleanValue($node['fileRename']) : false;
			$_fileDelete = isset($node['fileDelete']) ? CKFinder_Connector_Utils_Misc::booleanValue($node['fileDelete']) : false;

			$_role = isset($node['role']) ? $node['role'] : "*";
			$_resourceType = isset($node['resourceType']) ? $node['resourceType'] : "*";
			$_folder = isset($node['folder']) ? $node['folder'] : "/";

			$this->addACLEntry($_role, $_resourceType, $_folder,
			array(
			$_folderView ? CKFINDER_CONNECTOR_ACL_FOLDER_VIEW : 0,
			$_folderCreate ? CKFINDER_CONNECTOR_ACL_FOLDER_CREATE : 0,
			$_folderRename ? CKFINDER_CONNECTOR_ACL_FOLDER_RENAME : 0,
			$_folderDelete ? CKFINDER_CONNECTOR_ACL_FOLDER_DELETE : 0,
			$_fileView ? CKFINDER_CONNECTOR_ACL_FILE_VIEW : 0,
			$_fileUpload ? CKFINDER_CONNECTOR_ACL_FILE_UPLOAD : 0,
			$_fileRename ? CKFINDER_CONNECTOR_ACL_FILE_RENAME : 0,
			$_fileDelete ? CKFINDER_CONNECTOR_ACL_FILE_DELETE : 0,
			),
			array(
			$_folderView ? 0 : CKFINDER_CONNECTOR_ACL_FOLDER_VIEW,
			$_folderCreate ? 0 : CKFINDER_CONNECTOR_ACL_FOLDER_CREATE,
			$_folderRename ? 0 : CKFINDER_CONNECTOR_ACL_FOLDER_RENAME,
			$_folderDelete ? 0 : CKFINDER_CONNECTOR_ACL_FOLDER_DELETE,
			$_fileView ? 0 : CKFINDER_CONNECTOR_ACL_FILE_VIEW,
			$_fileUpload ? 0 : CKFINDER_CONNECTOR_ACL_FILE_UPLOAD,
			$_fileRename ? 0 : CKFINDER_CONNECTOR_ACL_FILE_RENAME,
			$_fileDelete ? 0 : CKFINDER_CONNECTOR_ACL_FILE_DELETE,
			)
			);
		}
	}

	/**
	 * Add ACL entry
	 *
	 * @param string $role role
	 * @param string $resourceType resource type
	 * @param string $folderPath folder path
	 * @param int $allowRulesMask allow rules mask
	 * @param int $denyRulesMask deny rules mask
	 * @access private
	 */
	private function addACLEntry($role, $resourceType, $folderPath, $allowRulesMask, $denyRulesMask)
	{

		if (!strlen($folderPath)) {
			$folderPath = '/';
		}
		else {
			if (substr($folderPath,0,1) != '/') {
				$folderPath = '/' . $folderPath;
			}

			if (substr($folderPath,-1,1) != '/') {
				$folderPath .= '/';
			}
		}

		$_entryKey = $role . "#@#" . $resourceType;

		if (array_key_exists($folderPath,$this->_aclEntries)) {
			if (array_key_exists($_entryKey, $this->_aclEntries[$folderPath])) {
				$_rulesMasks = $this->_aclEntries[$folderPath][$_entryKey];
				foreach ($_rulesMasks[0] as $key => $value) {
					$allowRulesMask[$key] |= $value;
				}
				foreach ($_rulesMasks[1] as $key => $value) {
					$denyRulesMask[$key] |= $value;
				}
			}
		}
		else {
			$this->_aclEntries[$folderPath] = array();
		}

		$this->_aclEntries[$folderPath][$_entryKey] = array($allowRulesMask, $denyRulesMask);
	}


	/**
	 * Get computed mask
	 *
	 * @param string $resourceType
	 * @param string $folderPath
	 * @return int
	 */
	public function getComputedMask($resourceType, $folderPath)
	{
		$_computedMask = 0;

		$_config =& CKFinder_Connector_Core_Factory::getInstance("Core_Config");
		$_roleSessionVar = $_config->getRoleSessionVar();

		$_userRole = null;
		if (strlen($_roleSessionVar) && isset($_SESSION[$_roleSessionVar])) {
			$_userRole = (string)$_SESSION[$_roleSessionVar];
		}
		if (!is_null($_userRole) && !strlen($_userRole)) {
			$_userRole = null;
		}

		$folderPath = trim($folderPath, "/");
		$_pathParts = explode("/", $folderPath);

		$_currentPath = "/";

		for($i = -1; $i < sizeof($_pathParts); $i++) {
			if ($i >= 0) {
				if (!strlen($_pathParts[$i])) {
					continue;
				}

				if (array_key_exists($_currentPath . '*/', $this->_aclEntries))
				$_computedMask = $this->mergePathComputedMask( $_computedMask, $resourceType, $_userRole, $_currentPath . '*/' );

				$_currentPath .= $_pathParts[$i] . '/';
			}

			if (array_key_exists($_currentPath, $this->_aclEntries)) {
				$_computedMask = $this->mergePathComputedMask( $_computedMask, $resourceType, $_userRole, $_currentPath );
			}
		}

		return $_computedMask;
	}

	/**
	 * merge current mask with folder entries
	 *
	 * @access private
	 * @param int $currentMask
	 * @param string $resourceType
	 * @param string $userRole
	 * @param string $path
	 * @return int
	 */
	private function mergePathComputedMask( $currentMask, $resourceType, $userRole, $path )
	{
		$_folderEntries = $this->_aclEntries[$path];

		$_possibleEntries = array();

		$_possibleEntries[0] = "*#@#*";
		$_possibleEntries[1] = "*#@#" . $resourceType;

		if (!is_null($userRole))
		{
			$_possibleEntries[2] = $userRole . "#@#*";
			$_possibleEntries[3] = $userRole . "#@#" . $resourceType;
		}

		for ($r = 0; $r < sizeof($_possibleEntries); $r++)
		{
			$_possibleKey = $_possibleEntries[$r];
			if (array_key_exists($_possibleKey, $_folderEntries))
			{
				$_rulesMasks = $_folderEntries[$_possibleKey];

				$currentMask |= array_sum($_rulesMasks[0]);
				$currentMask ^= ($currentMask & array_sum($_rulesMasks[1]));
			}
		}

		return $currentMask;
	}
}
